package com.ruoyi.project.socket.server;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.ruoyi.project.socket.decode.FrameDecoder;
import com.ruoyi.project.socket.decode.MsgPckDecode;
import com.ruoyi.project.socket.encode.MsgPckEncode;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.net.InetSocketAddress;
import java.util.Date;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class Server {

    private int port;
    private final Logger logger = LoggerFactory.getLogger(Server.class);
    private EventLoopGroup bossGroup,workerGroup,business;
    private Channel channel;


    public void run(String[] args,int port) throws InterruptedException {
        this.port = port;
        //开启服务器
        start();
        //注册钩子
        shutDownHook();
    }


    private void shutDownHook() {
        Runtime.getRuntime().addShutdownHook(new Thread(()-> {
            logger.info("服务器关闭，开始释放资源，记录设备下线时间");
            releaseResource();
        }));
    }

    private void releaseResource() {
        if(channel!=null){
            channel.closeFuture().syncUninterruptibly();
        }
        try {
            if(bossGroup!=null) {
                bossGroup.shutdownGracefully().sync();
            }
            if(workerGroup!=null) {
                workerGroup.shutdownGracefully().sync();
            }
            if(business!=null) {
                business.shutdownGracefully().sync();
            }
        } catch (InterruptedException e) {
            logger.info("bossGroup workerGroup business shutdown 异常");
            e.printStackTrace();
        }
    }

    private void start() throws InterruptedException {
        ChannelFuture future = null;
        final ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("sh-business-%d").setDaemon(false).build();
        // 接收客户端的TCP连接；向通信对端发送消息请求或者应答消息；
        bossGroup = new NioEventLoopGroup(1);
        //io线程
        workerGroup = new NioEventLoopGroup();
        //业务线程
        business = new NioEventLoopGroup(1000,threadFactory);
        try {
            //服务器启动类，把所有组件串起来
            ServerBootstrap server = new ServerBootstrap();
            server.group(bossGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .localAddress(new InetSocketAddress(port))
                    .childOption(ChannelOption.SO_SNDBUF,1024)
                    .childOption(ChannelOption.SO_RCVBUF,1024)
                    //客户端连接同时涌入
                    .option(ChannelOption.SO_BACKLOG,10000)
                    //复用Time Wait 连接
                    .childOption(ChannelOption.SO_REUSEADDR,true)
                    //-1 表示close()方法立即返回，但OS底层会将发送缓冲区全部发送到对端。
                    //.childOption(ChannelOption.SO_LINGER,-1)
                    //低延迟，禁用nagle优化算法，禁止优化成大块数据传输
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childOption(ChannelOption.RCVBUF_ALLOCATOR,
                            new AdaptiveRecvByteBufAllocator(1, 1024, Integer.MAX_VALUE))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        //Handler主要是做编解码和业务逻辑处理
                        //而我们接收数据期望收到的可不是字节，而是基本数据类型或者对象。所以要做编解码
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new IdleStateHandler(600,30,0, TimeUnit.SECONDS));
                            ch.pipeline().addLast("frameDecoder",new FrameDecoder(1024));
                            ch.pipeline().addLast("decoder", new MsgPckDecode());
                            ch.pipeline().addLast("encoder",new MsgPckEncode());
                        }
                    });
            future = server.bind().sync();
            channel = future.channel();
        } finally {
            if (future != null && future.isSuccess()) {
                logger.info("Netty server listening on port " + port + " and ready for connections...");
            } else {
                logger.error("Netty server start up Error!");
            }
        }
    }

}
